
use std::{
    collections::HashMap,
    fmt::Debug,
    fs::{self, File},
    io::{BufRead, BufReader},
};

use log::{info};
use mongodb::{bson, sync::Collection};
use mongodb::bson::Document;
use serde::{Deserialize, Serialize};
use serde_json::Value;

/// 日志信息及结构体
#[derive(Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct LogInfo {
    date: String,
    time: String,
    ip: String,
    user_name: String,
    user_id: String,
    request_id: String,
    api_name: String,
    uri: String,
    status: String,
    time_span: String,
    detail: String,
    headers: String,
    request: String,
    response: String,
    stack: String,
}

impl LogInfo {
    pub fn request(&self) -> &String {
        &self.request
    }

    pub fn response(&self) -> &String {
        &self.response
    }

    pub fn stack(&self) -> &String {
        &self.stack
    }

    pub fn api_name(&self) -> &String {
        &self.api_name
    }
    pub fn detail(&self) -> &String {
        &self.detail
    }

    /// 把LogInfo数据保存到mongodb
    pub fn insert(&self, collection: &Collection<Document>) {
        let loginfo_bson = bson::to_document(&self).map_err(|e| {
            info!("BSON转换失败: {}", e);
            e
        }).unwrap_or_default();

        match collection.insert_one(loginfo_bson, None) {
            Err(e) => info!("插入失败: {}, uri: {}, request_id: {}", e, &self.uri, &self.request_id),
            Ok(_) => info!("插入成功: uri: {}, request_id: {}", &self.uri, &self.request_id),
        }
    }

    pub fn parse(msg: &str, api_map: &HashMap<String, String>) -> LogInfo {
        let (header, mut msg_body) =
            msg
                .split_once("\n")
                .unwrap_or(("", ""));



        //首行处理
        let mut log_info = LogInfo::default();
        log_info.headers = header.to_string();
        let msg_headers_vals: Vec<&str> = header.split_whitespace().collect();
        //认为头部不合法忽略
        if msg_headers_vals.len() >= 5 {
            log_info.date = msg_headers_vals[0].to_string();
            log_info.time = msg_headers_vals[1].to_string();
            log_info.ip = msg_headers_vals[2].to_string();
            log_info.request_id = msg_headers_vals[3]
                .replace(['[', ']'], "")
                .to_string();
        } else if !header.is_empty() {
            //兼容首行不为空，又不是固定日志首行的情况
            info!("首行识别错误：{}", &header);
            msg_body = msg;
        }

        // 按**** 分成 json日志包，和stack调用堆栈
        match msg_body
            .split_once("\u{9658}\u{0003}\u{9658}")
            .or(msg_body.split_once("****"))
        {
            Some((msg_no_stack, stack)) => {
                match serde_json::from_str::<Value>(&msg_no_stack) {
                    Ok(mut json) => {
                        log_info.request = json["request"].to_string();
                        log_info.response = json["response"].to_string(); //返回结构

                        if let Value::Object(request) = &json["request"] {
                            if let Some(Value::String(uri)) = request.get("uri") {
                                log_info.uri = uri.to_string();
                            }
                            if let Some(Value::String(zwxm00)) = request.get("body").and_then(|b| b.get("zwxm00")) {
                                log_info.user_name = zwxm00.to_string();
                            }
                        }

                        if let Some(Value::String(str)) = json.get("timeSpan") {
                            log_info.time_span = str.to_string();
                        }

                        log_info.status = json["response"]["body"]["status"].to_string(); //接口返回值
                        //记录数超100的只显示100条
                        if let Value::Array(vec) = &json["response"]["body"]["payload"]["data"] {
                            if vec.len() > 100 {
                                json["response"]["body"]["payload"]["data"] = Value::from(
                                    format!("总记录：{} {}...", vec.len(), vec[0].to_string()),
                                );
                                log_info.response = json.to_string()
                            }
                        }
                        //泉二大部分统计查询和报表没有按 response -> body -> payload -> data 格式处理
                        if msg.len() > 1024 * 1024 * 16 {
                            info!("文档超过16M，request-id={}", &log_info.request_id);
                            log_info.response = "记录太大不显示".to_string();
                        }

                        //接口中文名获取
                        log_info.api_name = api_map.get(&log_info.uri)
                            .cloned()
                            .unwrap_or_else(|| "".to_string());

                        //新格式不返回detail
                        //log_info.detail = msg.to_string();
                    }
                    Err(e) => {
                        info!("JSON解析失败: {}-{}", e, &msg_no_stack);
                    }
                };

                log_info.stack = stack.to_string();
                if stack.len() > 1024 * 1024 * 10 {
                    log_info.stack = stack[..1024 * 20].to_string();
                }
            }
            None => {
                info!("日志格式不符合预期");
            }
        }
        log_info
    }

    pub fn parser_sql(stack: &str) -> String {
        let mut sql_flag = false;
        let mut now_sql = String::new();

        for line in stack.lines() {
            if line.contains("JdbcEntry:") || line.contains("jdbcCallTrace:") {
                sql_flag = true;
                now_sql.push_str(line.trim_start());
                now_sql.push('\n');
            } else if sql_flag {
                now_sql.push_str(line.trim_start());
            }
            if line.contains("}") {
                sql_flag = false;
                now_sql.push(']');
            }
        }
        now_sql = now_sql.replace("JdbcEntry:", "")
            .replace("jdbcCallTrace:", "")
            .replace("{", "")
            .replace("}", "")
            .replace("sql:'", "sql:[")
            .replace("', timeSpan:", ", timeSpan:");
        now_sql
    }

    //从离线trace日志文件加载内容，并输出LogInfo列表对象
    pub fn load_trace(log_path: &str, collection: &Collection<Document>) -> Vec<Document> {
        let api_map = LogInfo::load_api_map();
        info!("打开日志文件……");
        let file = File::open(&log_path).expect("无法打开日志文件");
        let reader = BufReader::new(file);
        let mut content = String::new();
        for line in reader.lines() {
            if let Ok(l) = line {
                content.push_str(&l);
                content.push('\n');
            }
        }
        let buf = content.replace("\r", "");
        let log_msgs: Vec<&str> = buf.split("\u{0003}\u{0003}\u{0003}\n").collect();
        info!("  共{}条日志", log_msgs.len());
        info!("  解析请求日志报文……");
        let mut log_info_list: Vec<Document> = Vec::new();
        for msg in log_msgs {
            //空行跳过
            if msg.trim().is_empty() {
                continue;
            }

            let log_info = LogInfo::parse(&msg, &api_map);
            log_info_list.push(bson::to_document(&log_info).unwrap_or_default());
        }
        info!("  解析完成，加载到mongodb……");
        if !log_info_list.is_empty() {
            collection.insert_many(log_info_list.clone(), None).expect("插入MongoDB失败");
        }

        info!("  加载日志结束。");

        log_info_list
    }

    //api字典加载
    pub fn load_api_map() -> HashMap<String, String> {
        info!("加载接口名称字典：");

        let mut api_map: HashMap<String, String> = HashMap::new();
        let api_content = fs::read_to_string("cross_interface.txt").expect("无法读取API配置文件");
        for line in api_content.lines() {
            if line.is_empty() {
                continue;
            }
            let api_vec: Vec<&str> = line.split_whitespace().collect();

            if api_vec.len() >= 2 {
                api_map.insert(
                    format!("/{}", api_vec[1]),
                    api_vec[0].to_string(),
                );
            }
        }
        info!("接口名称字典加载完成");
        api_map
    }
}